package com.yunxiao.cloud.oss.controller;

import com.yunxiao.cloud.common.base.BaseResultResponse;
import com.yunxiao.cloud.common.constant.ExceptionConstant;
import com.yunxiao.cloud.oss.constant.OssConstant;
import com.yunxiao.cloud.oss.entity.FileBodyModel;
import com.yunxiao.cloud.oss.hdfs.HadoopClient;
import com.yunxiao.cloud.oss.util.FileUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

/**
 * @description: FileController
 * @date: 2019/12/18 13:23
 * @author: yunxiao
 * @version: 1.0
 */
@Controller
@RequestMapping("file")
@Api(value = "Hadoop文件管理接口类", tags = "统一文件管理接口")
public class FileController {

    private static final Logger logger = LoggerFactory.getLogger(FileController.class);

    @Autowired
    private HadoopClient hadoopClient;

    /**
     * 上传文件
     *
     * @param uploadPath
     * @param file
     * @return
     */
    @ApiOperation(value = "上传文件", notes = "上传文件方法")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "uploadPath", value = "目标路径", required = true, dataType = "String", defaultValue = "/data/")
    })
    @ResponseBody
    @PostMapping("upload")
    public BaseResultResponse upload(@RequestParam String uploadPath, MultipartFile file) {
        if (file == null) {
            return BaseResultResponse.error("请上传文件");
        }

        boolean result = hadoopClient.copyFileToHdfs(false, true, FileUtil.multipartFileToFile(file).getPath(), uploadPath);
        if (result) {
            return BaseResultResponse.ok();
        } else {
            return BaseResultResponse.error("文件上传失败");
        }

    }

    /**
     * 下载文件
     *
     * @param path
     * @param fileName
     * @param response
     */
    @ApiOperation(value = "下载文件 - HttpServletResponse", notes = "下载文件方法")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "源文件路径", required = true, dataType = "String", defaultValue = "/data/"),
            @ApiImplicitParam(name = "fileName", value = "文件名称", required = true, dataType = "String", defaultValue = "project.png")
    })
    @PostMapping(value = "download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public BaseResultResponse download(@RequestParam String path, @RequestParam String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
        setHeader(fileName, response);

        OutputStream outputStream;
        try {
            outputStream = response.getOutputStream();
            boolean result = hadoopClient.download(path, fileName, outputStream);
            if (!result) {
                return BaseResultResponse.error(ExceptionConstant.HADOOP_DOWNLOAD_FAILED_CODE, ExceptionConstant.HADOOP_DOWNLOAD_FAILED_MSG);
            }
        } catch (IOException e) {

            logger.error("下载文件失败 ", e);
            return BaseResultResponse.error(ExceptionConstant.HADOOP_DOWNLOAD_FAILED_CODE, ExceptionConstant.HADOOP_DOWNLOAD_FAILED_MSG);
        }
        return BaseResultResponse.ok();
    }

    private void setHeader(String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
        String finalName = fileName;
        String[] fileNameArr = fileName.split(OssConstant.FILE_SEPARATION);
        if (fileNameArr.length > 1) {
            finalName = fileNameArr[fileNameArr.length - 1];
        }

        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Expires", "0");
        response.setHeader("charset", OssConstant.UTF_8);
        response.setHeader("Content-Transfer-Encoding", "binary");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(finalName, OssConstant.UTF_8));
        response.setHeader("x-frame-options", "SAMEORIGIN");
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader("X-XSS-Protection", "1; mode=block");
        response.setHeader("X-Content-Type-Options", "nosniff");
    }

    @ApiOperation(value = "获取完整文件路径", notes = "获取完整文件路径")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "源文件路径", required = true, dataType = "String", defaultValue = "/data/"),
            @ApiImplicitParam(name = "fileName", value = "文件名称", required = true, dataType = "String", defaultValue = "project.png")
    })
    @ResponseBody
    @PostMapping("getFullFilePath")
    public BaseResultResponse getFullFilePath(@RequestParam String path, @RequestParam String fileName) {
        String result = hadoopClient.getFilePath(path, fileName);
        FileBodyModel fileBodyModel = new FileBodyModel();
        fileBodyModel.setFilePath(result);
        fileBodyModel.setSuccess(true);
        return BaseResultResponse.ok(fileBodyModel);
    }


    /**
     * 创建目录
     *
     * @param folderPath
     * @return
     */
    @ApiOperation(value = "创建目录", notes = "创建目录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "folderPath", value = "目录名", required = true, dataType = "String", defaultValue = "/data/"),
    })
    @ResponseBody
    @PostMapping("mkdir")
    public BaseResultResponse mkdir(@RequestParam String folderPath) {
        boolean result = false;
        if (StringUtils.isNotEmpty(folderPath)) {
            result = hadoopClient.mkdir(folderPath, true);
        }
        FileBodyModel fileBodyModel = new FileBodyModel();
        fileBodyModel.setSuccess(result);

        return BaseResultResponse.ok(fileBodyModel);
    }

    /**
     * 目录信息
     *
     * @param path
     * @return
     */
    @ApiOperation(value = "获取目录信息", notes = "获取目录信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "源文件路径", required = true, dataType = "String", defaultValue = "/data/"),
    })
    @ResponseBody
    @PostMapping("getPathInfo")
    public BaseResultResponse<List<Map<String, Object>>> getPathInfo(@RequestParam String path) {
        try {
            return BaseResultResponse.ok(hadoopClient.getPathInfo(path));
        } catch (IOException e) {

            return BaseResultResponse.error("获取目录信息失败");
        }
    }

    /**
     * 获取目录下文件列表
     *
     * @param path
     * @return
     */
    @ApiOperation(value = "获取目录下文件列表", notes = "获取目录下文件列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "源文件路径", required = true, dataType = "String", defaultValue = "/data/"),
    })
    @ResponseBody
    @GetMapping("getFileList")
    public BaseResultResponse<List<Map<String, String>>> getFileList(@RequestParam String path) {
        if (StringUtils.isEmpty(path)) {
            return new BaseResultResponse<>("-1", "path is empty");

        }
        return BaseResultResponse.ok(hadoopClient.getFileList(path));
    }

    /**
     * 删除文件或文件夹
     *
     * @param path
     * @param fileName
     * @return
     */
    @ApiOperation(value = "删除文件或文件夹", notes = "删除文件或文件夹")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "源文件路径", required = true, dataType = "String", defaultValue = "/data/"),
            @ApiImplicitParam(name = "fileName", value = "文件名", required = false, dataType = "String", defaultValue = "project.png"),
    })
    @ResponseBody
    @PostMapping("rmdir")
    public BaseResultResponse rmdir(@RequestParam String path, @RequestParam(required = false) String fileName) {
        boolean result = hadoopClient.rmdir(path, fileName);
        FileBodyModel fileBodyModel = new FileBodyModel();
        fileBodyModel.setSuccess(result);
        return BaseResultResponse.ok(fileBodyModel);
    }


    /**
     * 读取文件内容 一般为文本文件
     *
     * @param filePath
     * @return
     */
    @ApiOperation(value = "读取文件内容", notes = "读取文件内容")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "filePath", value = "源文件路径", required = true, dataType = "String", defaultValue = "/data/project.txt"),
    })
    @ResponseBody
    @GetMapping("readFile")
    public BaseResultResponse readFile(@RequestParam String filePath) {
        return BaseResultResponse.ok((Object) hadoopClient.readFile(filePath));
    }

    /**
     * 文件或文件夹重命名
     *
     * @param oldName
     * @param newName
     * @return
     */
    @ApiOperation(value = "文件或文件夹重命名", notes = "文件或文件夹重命名")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "oldName", value = "原文件名", required = true, dataType = "String", defaultValue = "/data/project.txt"),
            @ApiImplicitParam(name = "newName", value = "新文件名", required = true, dataType = "String", defaultValue = "/data/project1.txt"),
    })
    @ResponseBody
    @PostMapping("renameFile")
    public BaseResultResponse renameFile(@RequestParam String oldName, @RequestParam String newName) {
        boolean result = hadoopClient.renameFile(oldName, newName);

        FileBodyModel fileBodyModel = new FileBodyModel();
        fileBodyModel.setSuccess(result);

        return BaseResultResponse.ok(fileBodyModel);
    }

    /**
     * 下载文件到本地(复制一下)
     *
     * @param path
     * @param downloadPath
     * @return
     */
    @ApiOperation(value = "下载文件到本地(复制一下)", notes = "下载文件到本地(复制一下)")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "path", value = "源路径", required = true, dataType = "String", defaultValue = "/data/project.txt"),
            @ApiImplicitParam(name = "downloadPath", value = "目标路径", required = true, dataType = "String", defaultValue = "/data/project1.txt"),
    })
    @ResponseBody
    @PostMapping("downloadFileToLocal")
    public BaseResultResponse downloadFileToLocal(@RequestParam String path, @RequestParam String downloadPath) {
        hadoopClient.downloadFileToLocal(path, downloadPath);
        return BaseResultResponse.ok();
    }

    /**
     * 复制文件
     *
     * @param sourcePath
     * @param targetPath
     * @return
     */
    @ApiOperation(value = "复制文件", notes = "复制文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "sourcePath", value = "原文件名", required = true, dataType = "String", defaultValue = "/data/project.txt"),
            @ApiImplicitParam(name = "targetPath", value = "新文件名", required = true, dataType = "String", defaultValue = "/data/project1.txt"),
    })
    @ResponseBody
    @PostMapping("copyFile")
    public BaseResultResponse copyFile(String sourcePath, String targetPath) {
        boolean result = hadoopClient.copyFile(sourcePath, targetPath);
        return BaseResultResponse.ok(result);
    }

    @ApiOperation(value = "下载文件-fileKey - HttpServletResponse", notes = "下载文件方法")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileKey", value = "文件唯一标志", required = true, dataType = "String", defaultValue = "/data/project.png")
    })
    @RequestMapping(value = "downloadFile", method = {RequestMethod.GET, RequestMethod.POST},
            produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public BaseResultResponse download(@RequestParam String fileKey, HttpServletResponse response) throws UnsupportedEncodingException {
        setHeader(fileKey, response);

        OutputStream outputStream;
        try {
            outputStream = response.getOutputStream();
            boolean result = hadoopClient.download("", fileKey, outputStream);
            if (!result) {
                return BaseResultResponse.error(ExceptionConstant.HADOOP_DOWNLOAD_FAILED_CODE, ExceptionConstant.HADOOP_DOWNLOAD_FAILED_MSG);
            }
        } catch (IOException e) {
            logger.error("下载文件失败 ", e);
            return BaseResultResponse.error(ExceptionConstant.HADOOP_DOWNLOAD_FAILED_CODE, ExceptionConstant.HADOOP_DOWNLOAD_FAILED_MSG);
        }
        return BaseResultResponse.ok();
    }


    /**
     * 上传文件
     *
     * @param file
     * @return
     */
    @ApiOperation(value = "上传文件-返回唯一key", notes = "上传文件方法")
    @ResponseBody
    @PostMapping("uploadFile")
    public BaseResultResponse upload(MultipartFile file) {
        String uploadPath = OssConstant.FILE_SEPARATION + System.currentTimeMillis() + OssConstant.FILE_SEPARATION;
        FileBodyModel bodyModel = new FileBodyModel();
        if (file == null) {
            return BaseResultResponse.error("请上传文件");
        }
        String filePath = FileUtil.multipartFileToFile(file).getPath();
        String fileName = FileUtil.multipartFileToFile(file).getName();

        boolean result = hadoopClient.copyFileToHdfs(false, true, filePath, uploadPath);
        if (result) {
            bodyModel.setSuccess(true);
            bodyModel.setPath(uploadPath);
            bodyModel.setFileName(fileName);
            bodyModel.setFilePath(uploadPath + fileName);
            bodyModel.setFileKey(uploadPath + fileName);
            return BaseResultResponse.ok(bodyModel);
        } else {
            return BaseResultResponse.error("文件上传失败");
        }
    }


    @ApiOperation(value = "下载文件-fileKey - HttpServletResponse", notes = "下载文件方法")
    @RequestMapping(value = "downloadFile/{fileKey1}/**", method = {RequestMethod.GET, RequestMethod.POST},
            produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public BaseResultResponse downloadStatic(HttpServletRequest request, @PathVariable("fileKey1") String fileKey1, HttpServletResponse response) throws UnsupportedEncodingException {
        String fileKey = extractPathFromPattern(request);
        setHeader(fileKey, response);

        OutputStream outputStream;
        try {
            outputStream = response.getOutputStream();
            boolean result = hadoopClient.download("", fileKey, outputStream);
            if (!result) {
                return BaseResultResponse.error(ExceptionConstant.HADOOP_DOWNLOAD_FAILED_CODE, ExceptionConstant.HADOOP_DOWNLOAD_FAILED_MSG);
            }
        } catch (IOException e) {
            logger.error("下载文件失败 ", e);
            return BaseResultResponse.error(ExceptionConstant.HADOOP_DOWNLOAD_FAILED_CODE, ExceptionConstant.HADOOP_DOWNLOAD_FAILED_MSG);
        }
        return BaseResultResponse.ok();
    }


    private String extractPathFromPattern(final HttpServletRequest request) {
        String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
        String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);

    }
}